package com.kotlinvben.service

import com.kotlinvben.entity.User
import com.kotlinvben.enumeration.Status
import com.kotlinvben.exception.BusinessErrorCode
import com.kotlinvben.exception.BusinessException
import com.kotlinvben.repository.DepartmentRepository
import com.kotlinvben.repository.RoleRepository
import com.kotlinvben.repository.UserRepository
import com.kotlinvben.utils.JwtUtils
import com.kotlinvben.utils.SecurityUtils
import com.kotlinvben.vo.UserLoginVo
import com.kotlinvben.vo.UserUpdateVO
import io.jsonwebtoken.ExpiredJwtException
import jakarta.persistence.criteria.Predicate
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.domain.Specification
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class UserService(
    private val userRepository: UserRepository,
    private val departmentRepository: DepartmentRepository,
    private val roleRepository: RoleRepository,
    private val jwtUtils: JwtUtils,
    private val passwordEncoder: PasswordEncoder,
    private val userCacheService: UserCacheService
) {
    fun getUserById(id: Long): User? {
        return userRepository.findById(id).orElse(null)
    }

    fun getUsers(
        pageable: Pageable,
        name: String? = null,
        email: String? = null,
        phone: String? = null,
        status: Int? = null,
    ): Page<User> {
        val spec = Specification<User> { root, _, cb ->
            val predicates = mutableListOf<Predicate>()

            name?.let {
                predicates.add(cb.like(root.get("name"), "%$it%"))
            }

            email?.let {
                predicates.add(cb.like(root.get("email"), "%$it%"))
            }

            phone?.let {
                predicates.add(cb.like(root.get("phone"), "%$it%"))
            }

            status?.let {
                predicates.add(cb.equal(root.get<Int>("status"), Status.ENABLED.code))
            }

            cb.and(*predicates.toTypedArray())
        }
        return userRepository.findAll(spec, pageable)
    }

    fun createUser(user: User, roleIds: List<Long>? = null): User {
        user.password = passwordEncoder.encode(user.password)

        user.department?.id?.let { deptId ->
            user.department = departmentRepository.findById(deptId).orElse(null)
        }

        roleIds?.let { ids ->
            val roles = roleRepository.findAllById(ids)
            user.roles = roles
        }

        return userRepository.save(user)
    }

    fun updateUser(id: Long, updateVO: UserUpdateVO): User {
        val user = getUserById(id) ?: throw RuntimeException("User not found")

        user.username = updateVO.username
        user.realName = updateVO.realName
        user.email = updateVO.email
        user.gender = updateVO.gender
        user.phone = updateVO.phone
        user.address = updateVO.address
        user.status = updateVO.status
        user.remark = updateVO.remark
        user.modifyBy = SecurityUtils.getCurrentUsername()

        updateVO.department?.id?.let { deptId ->
            user.department = departmentRepository.findById(deptId).orElse(null)
        }

        updateVO.roleIds?.let { roleIds ->
            val roles = roleRepository.findAllById(roleIds)
            user.roles = roles
        }

        return userRepository.save(user)
    }

    fun deleteUser(id: Long) {
        userRepository.softDelete(id)
    }

    fun updateUserStatus(id: Long, status: Int): User {
        val user = getUserById(id) ?: throw RuntimeException("User not found")
        user.status = status
        return userRepository.save(user)
    }

    fun login(loginVo: UserLoginVo): UserLoginVo {
        val user = userRepository.findByUsername(loginVo.username)
            ?: throw BusinessException(BusinessErrorCode.UNAUTHORIZED, "Invalid username or password")

        if (!passwordEncoder.matches(loginVo.password, user.password)) {
            throw BusinessException(BusinessErrorCode.UNAUTHORIZED, "Invalid username or password")
        }

        // 生成 token
        val token = jwtUtils.generateToken(user.id!!, user.username)
        loginVo.accessToken = token
        return loginVo
    }

    fun refreshToken(token: String): String {
        try {
            // 验证旧 token
            val claims = jwtUtils.validateToken(token)
            val userId = claims?.body?.get("userId") as Int

            // 获取用户信息
            val user = getUserById(userId.toLong())
                ?: throw BusinessException(BusinessErrorCode.NOT_FOUND, "User not found")

            // 生成新 token
            return jwtUtils.generateToken(user.id!!, user.username)
        } catch (e: ExpiredJwtException) {
            throw BusinessException(BusinessErrorCode.UNAUTHORIZED, "Token has expired")
        } catch (e: Exception) {
            throw BusinessException(BusinessErrorCode.UNAUTHORIZED, "Invalid token")
        }
    }

    fun logout(token: String) {
        try {
            // 验证 token
            val claims = jwtUtils.validateToken(token)
            val userId = claims?.body?.get("userId") as Int

            // 清除用户缓存
            userCacheService.deleteUserById(userId.toLong())
        } catch (e: Exception) {
            // 忽略 token 验证错误，因为登出操作应该总是成功
            log.warn("Error during logout: ${e.message}")
        }
    }

    companion object {
        private val log = org.slf4j.LoggerFactory.getLogger(UserService::class.java)
    }
}